//  Copyright (c) 2013年 mafengwo All rights reserved.
//  Created by hanfeng on 12-11-29.

#import "UIImage+Addition.h"
#import <ImageIO/ImageIO.h>
#import <MobileCoreServices/MobileCoreServices.h>
#import <Accelerate/Accelerate.h>

static const NSInteger GIF_IMAGE_HEADER_LENGTH = 4;
static inline double radians (double degrees) {return degrees * M_PI/180;}

@implementation UIImage (Addition)

+ (UIImage *)imageWithContentsOfResolutionIndependentFile:(NSString *)path
{
    return [[UIImage alloc] initWithContentsOfResolutionIndependentFile:path];
}

- (id)initWithContentsOfResolutionIndependentFile:(NSString *)path
{
    if ([UIScreen instancesRespondToSelector:@selector(scale)] && [[UIScreen mainScreen] scale] == 2.0)
	{
        NSString *path2x = [[path stringByDeletingLastPathComponent]
                            stringByAppendingPathComponent:[NSString stringWithFormat:@"%@@2x.%@",
                                                            [[path lastPathComponent] stringByDeletingPathExtension],
                                                            [path pathExtension]]];

        if ([[NSFileManager defaultManager] fileExistsAtPath:path2x])
		{
            return [self initWithContentsOfFile:path2x];
        }
    }

    return [self initWithContentsOfFile:path];
}

+ (UIImage *)imageFromColor:(UIColor *)color withSize:(CGSize)size {
    if (color == nil) return nil;

    CGFloat scale = 1.0;
    if ([UIScreen instancesRespondToSelector:@selector(scale)])
        scale *= [[UIScreen mainScreen] scale];

    UIGraphicsBeginImageContextWithOptions(size, NO, scale);

    [color set];
    UIRectFill(CGRectMake(0, 0, size.width, size.height));

    UIImage* imageResult = UIGraphicsGetImageFromCurrentImageContext();

    UIGraphicsEndImageContext();

    return imageResult;
}

- (UIImage*)centeredImageWithSize:(CGSize)newSize stretch:(BOOL)stretch
{
    return [self centeredImageWithSize:newSize withOffset:(CGPoint){0,0} stretch:stretch];
}

- (UIImage*)centeredImageWithSize:(CGSize)newSize withOffset:(CGPoint)offset stretch:(BOOL)stretch
{
    UIGraphicsBeginImageContextWithOptions(newSize, NO, self.scale);
    
    CGRect r = CGRectMake(0, 0, newSize.width, newSize.height);
    if (stretch == YES) {
        CGPoint lt = CGPointZero;
        if (self.size.width>self.size.height) {
            lt.x = -abs(self.size.width*(newSize.height/self.size.height)-newSize.width)/2.0f;
        }
        else {
            lt.y = -abs(self.size.height*(newSize.width/self.size.width)-newSize.height)/2.0f;
        }
        r = CGRectMake(lt.x,lt.y, newSize.width-lt.x*2, newSize.height-lt.y*2);
    }
    else {
        r = CGRectMake((newSize.width - self.size.width) / 2.0f,
                       (newSize.height - self.size.height) / 2.0f,
                       self.size.width,
                       self.size.height);
    }
    
    r = CGRectOffset(r, -offset.x, -offset.y);
    
    [self drawInRect:r];
    
    UIImage* imageResult = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    return imageResult;
}

+ (BOOL)isAnimatedGifImageWithImageData:(NSData*)imageData
{
    BOOL isAnimatedGif = NO;
    CGImageSourceRef imageSource = CGImageSourceCreateWithData((__bridge CFDataRef)(imageData), NULL);
    if (imageSource) {
        isAnimatedGif = (UTTypeConformsTo(CGImageSourceGetType(imageSource), kUTTypeGIF) && CGImageSourceGetCount(imageSource) > 1);
        CFRelease(imageSource);
        return isAnimatedGif;
    }
    return isAnimatedGif;
}

+ (BOOL)isGifImageWithImageData:(NSData*)imageData
{
    BOOL isGif = NO;
    CGImageSourceRef imageSource = CGImageSourceCreateWithData((__bridge CFDataRef)(imageData), NULL);
    if (imageSource) {
        isGif = UTTypeConformsTo(CGImageSourceGetType(imageSource), kUTTypeGIF);
        CFRelease(imageSource);
        return isGif;
    }
    const char* buf = (const char*)[imageData bytes];
    if (buf && buf[0] == 0x47 && buf[1] == 0x49 && buf[2] == 0x46 && buf[3] == 0x38) {
        isGif = YES;
    }
    return isGif;
}

+ (BOOL)isGifImageWithFilePath:(NSString *)filePath {
    NSFileHandle *fileHandle = [NSFileHandle fileHandleForReadingAtPath:filePath];
    if (fileHandle) {
        NSData *data = [fileHandle readDataOfLength:GIF_IMAGE_HEADER_LENGTH];
        return [self isGifImageWithImageData:data];
    }
    return NO;
}

- (UIImage *)imageWithBackgroundImage:(UIImage *)backgroundImage
{
    if (backgroundImage == nil) return self;

    CGFloat w = MAX(self.size.width, backgroundImage.size.width);
    CGFloat h = MAX(self.size.height, backgroundImage.size.height);
    CGSize size = CGSizeMake(w, h);
    CGFloat scale = 1.0;
    if ([UIScreen instancesRespondToSelector:@selector(scale)])
        scale *= [[UIScreen mainScreen] scale];

    UIGraphicsBeginImageContextWithOptions(size, NO, scale);

    CGSize bgsize = backgroundImage.size;
    [backgroundImage drawInRect:(CGRect){floorf((size.width-bgsize.width)/2),
        floorf((size.width-bgsize.width)/2),bgsize}];

    CGSize fgsize = self.size;
    [self drawInRect:(CGRect){floorf((size.width-fgsize.width)/2),
        floorf((size.width-fgsize.width)/2),fgsize}];

    UIImage* imageResult = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    return imageResult;
}

- (UIImage *)croppedImageWithRelativeFrame:(CGRect)frame
{
    return [self croppedImageWithRelativeFrame:frame fillInSize:self.size];
}

- (UIImage *)croppedImageWithRelativeFrame:(CGRect)frame fillInSize:(CGSize)fillSize
{
    CGFloat scale;
	CGSize newsize;
	CGSize size = self.size;
	if(size.width < fillSize.width && size.height < fillSize.height) {
		newsize = self.size;
	}
	else{
		if(size.width >= size.height){
            scale = fillSize.height/size.height;
			newsize.height = fillSize.height;
			newsize.width = ceilf(size.width*scale);
		}
		else {
			scale = fillSize.width/size.width;
			newsize.width = fillSize.width;
			newsize.height = ceilf(size.height*scale);
		}
	}
    
    frame.origin.x = MIN(MAX(frame.origin.x, 0.0), 1.0);
    frame.origin.y = MIN(MAX(frame.origin.y, 0.0), 1.0);
    frame.size.width = MIN(MAX(frame.size.width, 0.0), 1.0);
    frame.size.height = MIN(MAX(frame.size.height, 0.0), 1.0);
    
    CGRect r = (CGRect){newsize.width*frame.origin.x,
        newsize.height*frame.origin.y,
        newsize.width*frame.size.width,
        newsize.height*frame.size.height,
    };
    UIGraphicsBeginImageContextWithOptions(r.size, NO, self.scale);
    [self drawInRect:(CGRect){-r.origin.x,-r.origin.y,newsize}];
    UIImage* croppedImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    
    return croppedImage;
}

#pragma mark - Blur Effect


// http://blog.bubbly.net/2013/09/11/slick-tricks-for-ios-blur-effect/#QJbOofdRbxGGV9Ol.99
- (UIImage *)imageToApplyBlurWithRadius:(CGFloat)blurRadius insetWidth:(CGFloat)insetWidth
{
    @autoreleasepool {
        if (SYSTEM_VERSION_LESS_THAN(@"6.0")) {
            return self;
        }
        UIImage *progressImage = self;
        
        CIImage *originalImage = [CIImage imageWithCGImage:progressImage.CGImage];
        CIFilter *filter = [CIFilter filterWithName: @"CIGaussianBlur" keysAndValues:
                            kCIInputImageKey, originalImage,
                            @"inputRadius", @(blurRadius*progressImage.scale),
                            nil];
        CIImage *outputImage = filter.outputImage;
        
        @autoreleasepool {
            CIContext *context = [CIContext contextWithOptions:nil];
            
            CGFloat whRatio = originalImage.extent.size.width/originalImage.extent.size.height;
            
            CGFloat iw = whRatio > 1 ? insetWidth*whRatio : insetWidth;
            CGFloat ih = whRatio > 1 ? insetWidth : insetWidth/whRatio;
            CGRect extent = CGRectInset([originalImage extent], iw, ih);
            CGImageRef outImage = [context createCGImage:outputImage fromRect:extent];
            
            progressImage = [UIImage imageWithCGImage:outImage scale:self.scale orientation:self.imageOrientation];
            
            CGImageRelease(outImage);
        }
        
        return progressImage;
    }
}

- (UIImage *)blurryImage:(float)boxBlur{
    float blur = boxBlur;
    int boxSize = (int)(blur * 100);
    boxSize -= (boxSize % 2) + 1;
    
    CGImageRef img = self.CGImage;
    
    vImage_Buffer inBuffer, outBuffer;
    vImage_Error error;
    void *pixelBuffer;
    
    CGDataProviderRef inProvider = CGImageGetDataProvider(img);
    CFDataRef inBitmapData = CGDataProviderCopyData(inProvider);
    
    inBuffer.width = CGImageGetWidth(img);
    inBuffer.height = CGImageGetHeight(img);
    inBuffer.rowBytes = CGImageGetBytesPerRow(img);
    inBuffer.data = (void*)CFDataGetBytePtr(inBitmapData);
    
    pixelBuffer = malloc(CGImageGetBytesPerRow(img) * CGImageGetHeight(img));
    
    outBuffer.data = pixelBuffer;
    outBuffer.width = CGImageGetWidth(img);
    outBuffer.height = CGImageGetHeight(img);
    outBuffer.rowBytes = CGImageGetBytesPerRow(img);
    
    error = vImageBoxConvolve_ARGB8888(&inBuffer, &outBuffer, NULL,
                                       0, 0, boxSize, boxSize, NULL,
                                       kvImageEdgeExtend);
    
    
    if (error) {
        NSLog(@"error from convolution %ld", error);
    }
    
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    CGContextRef ctx = CGBitmapContextCreate(
                                             outBuffer.data,
                                             outBuffer.width,
                                             outBuffer.height,
                                             8,
                                             outBuffer.rowBytes,
                                             colorSpace,
                                             CGImageGetBitmapInfo(self.CGImage));
    
    CGImageRef imageRef = CGBitmapContextCreateImage (ctx);
    UIImage *returnImage = [UIImage imageWithCGImage:imageRef];
    
    //clean up
    CGContextRelease(ctx);
    CGColorSpaceRelease(colorSpace);
    
    free(pixelBuffer);
    CFRelease(inBitmapData);
    
    CGColorSpaceRelease(colorSpace);
    CGImageRelease(imageRef);
    return returnImage;
}


#pragma mark - Image Resize

+ (CGSize)stretchSize:(CGSize)thisSize withSize:(CGSize)aSize
{
	CGFloat scale;
	CGSize newsize;
	
	if(thisSize.width >= thisSize.height)
    {
        scale = aSize.width/thisSize.width;
        newsize.width = aSize.width;
        newsize.height = ceilf(thisSize.height*scale);
    }
    else
    {
        scale = aSize.height/thisSize.height;
        newsize.height = aSize.height;
        newsize.width = ceilf(thisSize.width*scale);
    }
	return newsize;
}

+ (CGSize)fitSize:(CGSize)thisSize inSize:(CGSize)aSize
{
	CGFloat scale;
	CGSize newsize;
	
	if(thisSize.width<aSize.width && thisSize.height < aSize.height)
	{
		newsize = thisSize;
	}
	else
	{
		if(thisSize.width >= thisSize.height)
		{
			scale = aSize.width/thisSize.width;
			newsize.width = aSize.width;
			newsize.height = ceilf(thisSize.height*scale);
		}
		else
		{
			scale = aSize.height/thisSize.height;
			newsize.height = aSize.height;
			newsize.width = ceilf(thisSize.width*scale);
		}
	}
	return newsize;
}

- (UIImage *)imageWithFitSize:(CGSize)size
{
	// calculate the fitted size
	size = [[self class] fitSize:self.size inSize:size];
    
	UIGraphicsBeginImageContextWithOptions(size, NO, self.scale);
    
	CGRect rect = CGRectMake(0, 0, size.width, size.height);
	[self drawInRect:rect];
	
	UIImage *newimg = UIGraphicsGetImageFromCurrentImageContext();
	UIGraphicsEndImageContext();
	
	return newimg;
}

+ (CGSize)fitSize:(CGSize)thisSize forWidth:(CGFloat)width
{
    CGFloat scale;
	CGSize newsize;
	
	if(thisSize.width <= width)
	{
		newsize = thisSize;
	}
	else
	{
        scale = width / thisSize.width;
		newsize.width = width;
        newsize.height = ceilf(scale * thisSize.height);
	}
	return newsize;
}

+ (CGSize)fitSize:(CGSize)thisSize forHeight:(CGFloat)height
{
    CGFloat scale;
	CGSize newsize;
	
	if(thisSize.height <= height)
	{
		newsize = thisSize;
	}
	else
	{
        scale = height / thisSize.height;
		newsize.height = height;
        newsize.width = ceilf(scale * thisSize.width);
	}
	return newsize;
}

- (UIImage *)imageWithFitWidth:(CGFloat)width
{
    // calculate the fitted size
	CGSize size = [[self class] fitSize:self.size forWidth:width];
	
	UIGraphicsBeginImageContextWithOptions(size, NO, self.scale);
    
	CGRect rect = CGRectMake(0, 0, size.width, size.height);
	[self drawInRect:rect];
	
	UIImage *newimg = UIGraphicsGetImageFromCurrentImageContext();
	UIGraphicsEndImageContext();
	
	return newimg;
}

- (UIImage *)imageWithFitHeight:(CGFloat)height
{
    // calculate the fitted size
	CGSize size = [[self class] fitSize:self.size forHeight:height];
	
	UIGraphicsBeginImageContextWithOptions(size, NO, self.scale);
    
	CGRect rect = CGRectMake(0, 0, size.width, size.height);
	[self drawInRect:rect];
	
	UIImage *newimg = UIGraphicsGetImageFromCurrentImageContext();
	UIGraphicsEndImageContext();
	
	return newimg;
}

- (UIImage *)imageWithRotatedDegrees:(CGFloat)degrees
{
    CGSize rotatedSize = CGSizeApplyAffineTransform(self.size, CGAffineTransformMakeRotation(degrees * M_PI / 180));
    
    rotatedSize.width = ABS(rotatedSize.width);
    rotatedSize.height = ABS(rotatedSize.height);
    
    CGFloat scale = self.scale;
    
    // Create the bitmap context
    UIGraphicsBeginImageContextWithOptions(rotatedSize, NO, scale);
    CGContextRef bitmap = UIGraphicsGetCurrentContext();
    
    // Move the origin to the middle of the image so we will rotate and scale around the center.
    CGContextTranslateCTM(bitmap, rotatedSize.width/2, rotatedSize.height/2);
    
    //   // Rotate the image context
    CGContextRotateCTM(bitmap, degrees * M_PI / 180);
    
    // Now, draw the rotated/scaled image into the context
    CGContextScaleCTM(bitmap, 1.0, -1.0);
    CGContextDrawImage(bitmap, CGRectMake(-self.size.width / 2,
                                          -self.size.height / 2,
                                          self.size.width,
                                          self.size.height),
                       [self CGImage]);
    
    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return newImage;
}

#pragma mark - AssetLibrary

+ (UIImage *)fullSizeImageForAssetRepresentation:(ALAssetRepresentation *)assetRepresentation
{
    UIImage *result = nil;
    @autoreleasepool {
        NSData *data = nil;
        
        uint8_t *buffer = (uint8_t *)malloc(sizeof(uint8_t)*[assetRepresentation size]);
        if (buffer != NULL) {
            NSError *error = nil;
            NSUInteger bytesRead = [assetRepresentation getBytes:buffer fromOffset:0 length:[assetRepresentation size] error:&error];
            data = [NSData dataWithBytes:buffer length:bytesRead];
            
            free(buffer);
        }
        
        if ([data length])
        {
            CGImageSourceRef sourceRef = CGImageSourceCreateWithData((__bridge CFDataRef)data, nil);
            
            NSMutableDictionary *options = [NSMutableDictionary dictionary];
            
//            [options setObject:(id)kCFBooleanTrue forKey:(id)kCGImageSourceShouldAllowFloat];
//            [options setObject:(id)kCFBooleanTrue forKey:(id)kCGImageSourceCreateThumbnailFromImageAlways];
//            [options setObject:(id)[NSNumber numberWithFloat:640.0f] forKey:(id)kCGImageSourceThumbnailMaxPixelSize];
//            [options setObject:(id)kCFBooleanTrue forKey:(id)kCGImageSourceCreateThumbnailWithTransform];
            
            CGImageRef imageRef = CGImageSourceCreateImageAtIndex(sourceRef, 0, (__bridge CFDictionaryRef)options);
            
            if (imageRef) {
                result = [UIImage imageWithCGImage:imageRef scale:[assetRepresentation scale] orientation:(UIImageOrientation)[assetRepresentation orientation]];
                CGImageRelease(imageRef);
            }
            
            if (sourceRef)
                CFRelease(sourceRef);
        }
    }
    return result;
}

-(UIImage *)scaleToWidth:(CGFloat)width
{
    UIImage *scaledImage = self;
    if (self.size.width != width) {
        CGFloat height = floorf(self.size.height * (width / self.size.width));
        CGSize size = CGSizeMake(width, height);
        
        // Create an image context
        UIGraphicsBeginImageContext(size);
        
        // Draw the scaled image
        [self drawInRect:CGRectMake(0.0f, 0.0f, size.width, size.height)];
        
        // Create a new image from context
        scaledImage = UIGraphicsGetImageFromCurrentImageContext();
        
        // Pop the current context from the stack
        UIGraphicsEndImageContext();
    }
    // Return the new scaled image
    return scaledImage;
}

@end
